import { Injectable, Logger, NestMiddleware } from "@nestjs/common";
import * as Bowser from "bowser";
import { Request, Response } from "express";
import { ClsService } from "nestjs-cls";

// import { OperationLogService } from "@app/modules/operation-log/operation-log.service";

interface GeoData {
    code: string;
    data: {
        continent: string;
        country: string;
        zipcode: string;
        timezone: string;
        accuracy: string;
        owner: string;
        isp: string;
        source: string;
        areacode: string;
        adcode: string;
        asnumber: string;
        lat: string;
        lng: string;
        radius: string;
        prov: string;
        city: string;
        district: string;
    };
    charge: boolean;
    msg: string;
    ip: string;
    coordsys: string;
}

// {
//     code: "Success",
//     data: {
//         continent: "亚洲",
//         country: "新加坡",
//         zipcode: "04",
//         timezone: "UTC+8",
//         accuracy: "城市",
//         owner: "甲骨文软件系统有限公司",
//         isp: "甲骨文软件系统有限公司",
//         source: "数据挖掘",
//         areacode: "SG",
//         adcode: "",
//         asnumber: "31898",
//         lat: "",
//         lng: "",
//         radius: "",
//         prov: "新加坡",
//         city: "新加坡",
//         district: ""
//     },
//     charge: false,
//     msg: "查询成功",
//     ip: "129.150.38.255",
//     coordsys: "WGS84"
// };

// 请求日志应该包含的关键信息
// interface RequestLog {
//     timestamp: string;
//     method: string;
//     url: string;
//     statusCode: number;
//     responseTime: number;
//     userAgent: string;
//     ip: string;
//     userId?: string;
//     requestId: string; // 用于链路追踪
//     params?: any; // 谨慎记录敏感参数
//     bodySize?: number;
//     responseSize?: number;
// }

@Injectable()
export class RequestLogMiddleware implements NestMiddleware {
    constructor(private readonly clsService: ClsService) {}

    private readonly logger = new Logger(RequestLogMiddleware.name);

    async use(req: Request, res: Response, next: () => void) {
        // 生产环境不记录请求日志
        if (process.env.NODE_ENV === "production") {
            return next();
        }
        // 解析用户代理，获取操作系统和浏览器信息
        const { os, browser } = Bowser.parse(req.headers["user-agent"]);
        // 请求的基础信息
        const loggerInfo = {
            username: (req.user as any)?.username ?? "",
            ip: req.ip,
            address: "",
            os: os.name,
            browser: browser.name,
            status: 0,
            module: "",
            operation: "",
            taketime: 0
        };

        // 根据 IP 查询请求来源的地理位置
        if (this.isLocalIpAddress(loggerInfo.ip)) {
            loggerInfo.address = "本地";
        } else {
            const isv6 = loggerInfo.ip.indexOf("::ffff:");
            loggerInfo.ip = isv6 > -1 ? loggerInfo.ip.substring(isv6 + 7) : loggerInfo.ip;
            const ipUrl = `https://qifu-api.baidubce.com/ip/geo/v1/district?ip=${loggerInfo.ip}`;
            try {
                const res = await fetch(ipUrl, { method: "get" });
                const {
                    code,
                    data: { prov, city, district }
                } = (await res.json()) as GeoData;

                loggerInfo.address = code === "Success" ? prov + city + district : "未知";
            } catch {
                loggerInfo.address = "未知";
            }
        }

        req["loggerInfo"] = loggerInfo;
        // 打印请求日志
        this.logger.debug(`➡️  >>>>>>>>> ${req.method} ${req.originalUrl}`);
        this.logger.debug(`Request query: ${JSON.stringify(req.query)}`);
        // this.logger.debug(`Request params: ${JSON.stringify(req.params)}`);
        this.logger.debug(`Request body: ${JSON.stringify(req.body)}`);
        this.logger.debug(`Request ID: ${req.id}`);
        this.logger.debug(`Request IP: ${loggerInfo.ip}, Address: ${loggerInfo.address}`);
        this.logger.debug(`User Agent: ${req.headers["user-agent"]}`);
        this.logger.debug(`OS: ${loggerInfo.os}, Browser: ${loggerInfo.browser}`);
        // this.logger.debug(`Request headers: ${JSON.stringify(req.headers)}`);

        const startTime = Date.now();

        // 调用下一个中间件或控制器处理请求
        res.on("finish", () => {
            // 计算并打印响应时间
            const taketime = Date.now() - startTime;
            this.logger.debug(`${res.statusCode} - Taketime: ${taketime}ms`);
            if (taketime > 3000) {
                this.logger.warn(`⚠️  Slow request detected: ${req.method} ${req.originalUrl} took ${taketime}ms`);
            }
            // 中间件无法获取响应体内容，需要在控制器中手动使用 res.locals.responseData 保存响应数据才行
            // 要记录响应体内容，可以使用拦截器来捕获响应数据，nestjs 的拦截器可以在响应发送前访问和修改响应数据
            // 拦截器就是 nest 为处理请求/响应数据而设计的
            // this.logger.debug(`Response body: ${res.locals?.responseData || "Not available"}`);
            this.logger.debug(`⬅️  <<<<<<<<<<<<<<<<<<<<<<<<<<<`);
            this.logger.debug("\n");

            // loggerInfo.username = (req.user as any)?.username ?? "";
            // loggerInfo.status = [200, 201, 304].includes(res.statusCode) ? 1 : 0;
            // loggerInfo.taketime = taketime;
        });

        next();
    }

    /**
     * 判断是否为本地IP地址
     * @param ip ip地址
     * @returns boolean
     */
    private isLocalIpAddress(ip: string) {
        const ipParts = ip.split(".").map(Number);

        // 检查是否为环回地址
        if (ip === "127.0.0.1" || ip === "::1") {
            return true;
        }

        // 检查是否为A、B、C类私有地址
        if (
            ipParts[0] === 10 ||
            (ipParts[0] === 172 && ipParts[1] >= 16 && ipParts[1] <= 31) ||
            (ipParts[0] === 192 && ipParts[1] === 168)
        ) {
            return true;
        }

        return false;
    }
}
